#include "pr/TrainingSet.h"
#include <stdexcept>

namespace pr {

UniquePattern TrainingSet::NullPattern = UniquePattern(0, Pattern(), 0);

TrainingSet::TrainingSet(std::size_t numFeatures)
  : numFeatures_(numFeatures)
  , numPatterns_(0)
{
}

void TrainingSet::add(const Pattern& pattern, std::size_t classId)
{
  if (pattern.featureCount() != numFeatures_)
    throw std::invalid_argument("Cannot add a pattern with wrong feature size.");

  bool addedToExistingPatternSet = false;
  for (iterator it = set_.begin(); it != set_.end(); ++it)
  {
    if (it->classId() == classId)
    {
      it->add(UniquePattern(idGenerator_.nextId(), pattern, classId));
      addedToExistingPatternSet = true;
      break;
    }
  }
  if (!addedToExistingPatternSet)
  {
    PatternSet pSet(classId);
    pSet.add(UniquePattern(idGenerator_.nextId(), pattern, classId));
    set_.push_back(pSet);
  }
  ++numPatterns_;
}

void TrainingSet::transform(Pattern::Operation& operation)
{
  if (set_.size() == 0)
    return;

  for (iterator it = set_.begin(); it != set_.end(); ++it)
  {
    for (std::size_t i = 0; i < it->patternCount(); ++i)
    {
      operation((*it)[i].Value);
    }
  }
}

void TrainingSet::clear()
{
  numPatterns_ = 0;
  set_.clear();
  idGenerator_ = IdGenerator();
}

std::size_t TrainingSet::featureCount() const
{
  return numFeatures_;
}

std::size_t TrainingSet::patternCount() const
{
  return numPatterns_;
}

std::size_t TrainingSet::classCount() const
{
  return set_.size();
}

const UniquePattern& TrainingSet::getPatternById(std::size_t patternId)
{
  for (iterator it = set_.begin(); it != set_.end(); ++it)
  {
    for (std::size_t index = 0; index < it->patternCount(); ++index)
    {
      const UniquePattern& p = (*it)[index];
      if (p.Id == patternId)
      {
        return p;
      }
    }
  }
  return NullPattern;
}

UniquePattern& TrainingSet::getPattern(std::size_t index)
{
  if (index >= numPatterns_)
    throw std::out_of_range("Index too big.");

  for (iterator it = set_.begin(); it != set_.end(); ++it)
  {
    if (index < it->patternCount())
    {
      return (*it)[index];
    }
    else
      index -= it->patternCount();
  }
  throw std::logic_error("");
}

const UniquePattern& TrainingSet::getPattern(std::size_t index) const
{
  if (index >= numPatterns_)
    throw std::out_of_range("Index too big.");

  for (const_iterator it = set_.begin(); it != set_.end(); ++it)
  {
    if (index < it->patternCount())
    {
      return (*it)[index];
    }
    else
      index -= it->patternCount();
  }
  throw std::logic_error("");
}


TrainingSet::PatternSet::PatternSet(std::size_t classId)
  :  classId_(classId)
{
}

void TrainingSet::PatternSet::add(const UniquePattern& pattern)
{
  patterns_.push_back(pattern);
}

UniquePattern& TrainingSet::PatternSet::operator[](std::size_t i)
{
  return patterns_[i];
}

const UniquePattern& TrainingSet::PatternSet::operator[](std::size_t i) const
{
  return patterns_[i];
}

} // namespace pr
